package edu.hawaii.teams.halealohacli.command;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Pattern;
import javax.xml.datatype.XMLGregorianCalendar;
import org.wattdepot.client.BadXmlException;
import org.wattdepot.client.MiscClientException;
import org.wattdepot.client.NotAuthorizedException;
import org.wattdepot.client.ResourceNotFoundException;
import org.wattdepot.client.WattDepotClient;

/**
 * Prints a timestamp, the current power for the source every interval,
 * and whether or not the power conservation goal is met.
 * 
 * The set-baseline command must be called on the same tower or lounge 
 * before monitor-goal is called; otherwise, an Exception is thrown.
 * 
 * @author Marifel Barbasa
 *
 */
public class MonitorGoal implements Command {
  
  /**
   * Returns a timestamp, the current power for the source every interval,
   * and whether or not the power conservation goal is met.
   * 
   * @param client A WattDepotClient to find the source in.
   * @param details String instances identifying the source and date to examine.
   * @return a String output specifying the current power and whether or not the
   *         baseline goal was met, for each interval (given by the user)
   * @throws Exception Handles any problems that may occur.
   * 
   */
  @Override
  public String getData(final WattDepotClient client, final String... details) throws Exception {
    // Constant double used to convert from watt to kilowatt.
    final double KILO = 1000.0;
    final StringBuilder buffer = new StringBuilder();
    final String currentSource = details[0];
    String givenGoal = details[1]; // An integer between 1 and 99
    final String givenInterval = details[2]; // An integer greater than 0
    final double goal = 1 - (Integer.parseInt(givenGoal) / 100); // In decimal form
    long interval = 1000 * (Long.parseLong(givenInterval));

    try {
      /**
       * Creates new timer task to be executed at each interval. Will use the
       * getLatestPowerConsumed method as the "task".
       */
      TimerTask task = new TimerTask() {
        
        /**
         * Retrieves the latest power from the current source and
         * determines whether or not this power meets the baseline goal.
         */
        @Override
        public void run() {
          try {
            double[] baseline = new double[24];
            for (int i = 0; i < 24; i++) {
              baseline[i] = Double.parseDouble(details[i + 3]) / KILO;
            }
            double power = client.getLatestPowerConsumed(details[0]) / KILO;

            // Create XMLGregorianCalendar to retrieve the current time and date.
            XMLGregorianCalendar timeNow =
                client.getLatestSensorData(currentSource).getTimestamp();
            
            String dt = String.valueOf(timeNow);
            int indexT = dt.indexOf('T'); // 'T' separates the date from the time
            // Acquire "HH:mm:ss"
            String time = dt.substring(indexT + 1, dt.indexOf('.'));
            
            boolean goalIsMet = false;
            String theBaseline = "";
            String timePeriod = "";
            String format = "%.1f";
            // 12 AM to before 1 AM
            if (Pattern.matches("00:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[0] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[0]);
              timePeriod = "12 AM to before 1 AM";
            }
            // 1 AM to before 2 AM
            else if (Pattern.matches("01:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[1] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[1]);
              timePeriod = "1 AM to before 2 AM";
            }
            // 2 AM to before 3 AM
            else if (Pattern.matches("02:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[2] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[2]);
              timePeriod = "2 AM to before 3 AM";
            }
            // 3 AM to before 4 AM
            else if (Pattern.matches("03:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[3] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[3]);
              timePeriod = "3 AM to before 4 AM";
            }
            // 4 AM to before 5 AM
            else if (Pattern.matches("04:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[4] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[4]);
              timePeriod = "4 AM to before 5 AM";
            }
            // 5 AM to before 6 AM
            else if (Pattern.matches("05:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[5] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[5]);
              timePeriod = "5 AM to before 6 AM";
            }
            // 6 AM to before 7 AM
            else if (Pattern.matches("06:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[6] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[6]);
              timePeriod = "6 AM to before 7 AM";
            }
            // 7 AM to before 8 AM
            else if (Pattern.matches("07:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[7] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[7]);
              timePeriod = "7 AM to before 8 AM";
            }
            // 8 AM to before 9 AM
            else if (Pattern.matches("08:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[8] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[8]);
              timePeriod = "8 AM to before 9 AM";
            }
            // 9 AM to before 10 AM
            else if (Pattern.matches("09:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[9] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[9]);
              timePeriod = "9 AM to before 10 AM";
            }
            // 10 AM to before 11 AM
            else if (Pattern.matches("10:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[10] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[10]);
              timePeriod = "10 AM to before 11 AM";
            }
            // 11 AM to before 12 PM
            else if (Pattern.matches("11:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[11] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[11]);
              timePeriod = "11 AM to before 12 PM";
            }
            // 12 PM to before 1 PM
            else if (Pattern.matches("12:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[12] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[12]);
              timePeriod = "12 PM to before 1 PM";
            }
            // 1 PM to before 2 PM
            else if (Pattern.matches("13:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[13] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[13]);
              timePeriod = "1 PM to before 2 PM";
            }
            // 2 PM to before 3 PM
            else if (Pattern.matches("14:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[14] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[14]);
              timePeriod = "2 PM to before 3 PM";
            }
            // 3 PM to before 4 PM
            else if (Pattern.matches("15:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[15] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[15]);
              timePeriod = "3 PM to before 4 PM";
            }
            // 4 PM to before 5 PM
            else if (Pattern.matches("16:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[16] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[16]);
              timePeriod = "4 PM to before 5 PM";
            }
            // 5 PM to before 6 PM
            else if (Pattern.matches("17:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[17] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[17]);
              timePeriod = "5 PM to before 6 PM";
            }
            // 6 PM to before 7 PM
            else if (Pattern.matches("18:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[18] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[18]);
              timePeriod = "6 PM to before 7 PM";
            }
            // 7 PM to before 8 PM
            else if (Pattern.matches("19:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[19] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[19]);
              timePeriod = "7 PM to before 8 PM";
            }
            // 8 PM to before 9 PM
            else if (Pattern.matches("20:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[20] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[20]);
              timePeriod = "8 PM to before 9 PM";
            }
            // 9 PM to before 10 PM
            else if (Pattern.matches("21:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[21] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[21]);
              timePeriod = "9 PM to before 10 PM";
            }
            // 10 PM to before 11 PM
            else if (Pattern.matches("22:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[22] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[22]);
              timePeriod = "10 PM to before 11 PM";
            }
            // 11 PM to before 12 AM
            else if (Pattern.matches("23:[0-9][0-9]:[0-9][0-9]", time)) {
              if (power <= baseline[23] * goal) {
                goalIsMet = true;
              }
              theBaseline = String.format(format, baseline[23]);
              timePeriod = "11 PM to before 12 AM";
            }
            
            buffer.append("The current power consumption is: " +
                String.format(format, power) + " kW\n");
            if (goalIsMet) {
              buffer.append("The goal of " + theBaseline + " kW for the time period " +
                  timePeriod + " was met.  Good job!" + "\n");
            }
            else {
              buffer.append("The goal of " + theBaseline + " kW for the time period " +
                  timePeriod + " was NOT met.  Go conserve!" + "\n");
            }
          }
          catch (NotAuthorizedException e) {
            buffer.append("Error: You are not authorized to retrieve the data.\n\n");
          }
          catch (IllegalArgumentException e) {
            buffer.append("Error: An error occurred in using the XMLGregorianCalendar class.\n\n");
          }
          catch (BadXmlException e) {
            buffer.append("Error: An error occurred in the XML from the server.\n\n");
          }
          catch (MiscClientException e) {
            buffer.append("Error: An error occurred while retrieving data.\n\n");
          }
          catch (ResourceNotFoundException e) {
            buffer.append("Error: The source could not be found.\n\n");
          }
        }
      };
      
      // Create XMLGregorianCalendar to retrieve the current time and date.
      XMLGregorianCalendar timeNow = client.getLatestSensorData(currentSource).getTimestamp();
      Date startTime = timeNow.toGregorianCalendar().getTime();
      
      Timer clock = new Timer();
      clock.schedule(task, startTime, interval);
      Thread.sleep(interval);
      
      // Terminates the timer
      clock.cancel();
    }
    catch (NumberFormatException e) {
      buffer.append("Error: An error occurred while parsing the goal or interval.\n\n");
    }
    catch (StringIndexOutOfBoundsException e) {
      buffer.append("Error: An error occurred while parsing the date; "
          + "the formatting is incorrect.\n\n");
    }
    catch (IllegalArgumentException e) {
      buffer.append("Error: An error occurred in using the XMLGregorianCalendar class.\n\n");
    }
    catch (BadXmlException e) {
      buffer.append("Error: An error occurred in the XML from the server.\n\n");
    }
    catch (MiscClientException e) {
      buffer.append("Error: An error occurred while retrieving data.\n\n");
    }
    catch (ResourceNotFoundException e) {
      buffer.append("Error: The source " + currentSource + " could not be found.\n\n");
    }
    return buffer.toString();
  }

}
